﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using System.Diagnostics;

namespace ChessEst1
{
    class Program
    {
        static void Main(string[] args)
        {

            bool isLoop = true;
            bool isProgramParamsOK = true;
            string ContinueYorNString = String.Empty;

            DisplayClass myDisplayClass0 = new DisplayClass();

            string m1 = String.Empty;
            HelperClass1 myHelperClass = new HelperClass1();  //class used downstream
            int InputEloPlayerIDInteger = -1; //used downstream for subject Player and passed into function
            int IterationsTotal = 0;
            int NumGamesPlayed = 0;
            
            double stdDev = 0.0; //std. dev used downstream for all players and passed into function
            double mean = 0.0; //mean of all players used downstream for all players and passed into function

            do
            {
                myDisplayClass0.Display1();
                // now check out if user wants to abort after reading the introduction screen upstream
                ContinueYorNString = String.Empty;
                try
                {
                    ContinueYorNString = Console.ReadLine();
                }
                catch (System.IO.IOException ex1)
                {
                    Console.WriteLine(ex1.InnerException.Message);
                }
                catch (Exception ex2)
                {
                    Console.WriteLine(ex2.InnerException.Message);
                }


                if (ContinueYorNString == "Q" || ContinueYorNString == "q")
                {
                    isLoop = false;
                    isProgramParamsOK = false;
                }
                if (isLoop == false)
                { break; }

                //
                m1 = "Would you like more detailed information about the program?";
                Console.WriteLine(m1);
                m1 = "(type Y = Yes and hit RETURN, or type RETURN for N = No (Default))";
                Console.WriteLine(m1);
                try
                {
                    ContinueYorNString = Console.ReadLine();
                }
                catch (System.IO.IOException ex1)
                {
                    Console.WriteLine(ex1.InnerException.Message);
                }
                catch (Exception ex2)
                {
                    Console.WriteLine(ex2.InnerException.Message);
                }
                if (ContinueYorNString == "Y" || ContinueYorNString == "y")
                {
                    myDisplayClass0.Display2();
                }
                
                //
                int NumPlayers = 0;
                double InputEloPlayer = 0.0; //converted to int later, so not important to pass downstream 
                string InputMeanStr = String.Empty; string InputStdDevStr = String.Empty;
                string NumPlayersStr = String.Empty; string IterationsTotalStr = String.Empty;
                string InputEloPlayerStr = String.Empty; string NumGamesPlayedStr = String.Empty;

                m1 = "Input mean Elo of all players and press Enter key";
                Console.WriteLine(m1);
                m1 = "(Hit ENTER to give the default mean Elo = 2000)";
                Console.WriteLine(m1);
                try
                {
                    InputMeanStr = Console.ReadLine();
                }
                catch (System.IO.IOException ex1)
                {
                    Console.WriteLine(ex1.InnerException.Message);                    
                }
                catch (Exception ex2)
                {
                    Console.WriteLine(ex2.InnerException.Message);
                }

                //Console.WriteLine();
                m1 = "Input Standard Deviation (sigma) of all players and press Enter key";
                Console.WriteLine(m1);
                m1 = "(Hit ENTER to give the default Std. Dev = 100)";
                Console.WriteLine(m1);
 
                try
                {
                    InputStdDevStr = Console.ReadLine();
                }
                catch (System.IO.IOException ex1)
                {
                    Console.WriteLine(ex1.InnerException.Message);
                }
                catch (Exception ex2)
                {
                    Console.WriteLine(ex2.InnerException.Message);
                }

                if (InputMeanStr == String.Empty)
                {
                    InputMeanStr = "2000";
                }

                bool isResult01InputMean = Double.TryParse(InputMeanStr, out mean);
                if (true != isResult01InputMean)
                {
                    Console.WriteLine("could not parse InputMean");
                    isLoop = false;
                    isProgramParamsOK = false;
                }
                if (mean <= 0.0 || mean > 3000.00)
                {
                    Console.WriteLine("mean is not greater than zero or more than 3000");
                    isLoop = false;
                    isProgramParamsOK = false;
                }
                if (isLoop == false)
                { break; }


                if (InputStdDevStr == String.Empty)
                {
                    InputStdDevStr = "100";
                }

                bool isResult02InputStdDev = Double.TryParse(InputStdDevStr, out stdDev);
                if (isResult02InputStdDev != true)
                {
                    Console.WriteLine("could not parse Std. Dev.");
                    isLoop = false;
                    isProgramParamsOK = false;
                }
                if (stdDev < 1.0)
                {
                    Console.WriteLine("Std. Dev too small, less than 1");
                    isLoop = false;
                    isProgramParamsOK = false;
                }
                if (isLoop == false)
                { break; }

                Console.WriteLine("The population mean is: {0:0.0}, and std Dev: {1:0.00}", mean, stdDev);
                string mean_stdDev_string = String.Format("The population mean is: {0:0.0}, and std Dev: {1:0.00}", mean, stdDev);
                myHelperClass.ConsoleOutputStrings.Add(mean_stdDev_string);

                /////////////
                //now enter player Elo
                Console.WriteLine();
                m1 = "Input the single Player Elo (input # and press Enter key)";
                Console.WriteLine(m1);
                m1 = "(Hit ENTER to give a default value of 1800 Elo)";
                Console.WriteLine(m1);

                try
                {
                    InputEloPlayerStr = Console.ReadLine();
                }
                catch (System.IO.IOException ex1)
                {
                    Console.WriteLine(ex1.InnerException.Message);
                    isProgramParamsOK = false;
                }
                catch (Exception ex2)
                {
                    Console.WriteLine(ex2.InnerException.Message);
                    isProgramParamsOK = false;
                }
                // 
                if (InputEloPlayerStr == String.Empty)
                {
                    InputEloPlayerStr = "1800";
                }

                bool isResult05InputEloPlayer = Double.TryParse(InputEloPlayerStr, out InputEloPlayer);
                if (isResult05InputEloPlayer != true)
                {
                    Console.WriteLine("could not parse player Input Elo, pls enter another number");
                    isLoop = false;
                    isProgramParamsOK = false;
                }
                if (isLoop == false) { break; }

                double StandardDeviationRangeFromInputEloMaximum = 3.0 * stdDev; //make sure input Elo is not more than 3x Sigma--otherwise unlikely any reasonable answer will emerge
                double MaxDiff_InputElo_mean_ = Math.Abs(InputEloPlayer - mean);

                if ((InputEloPlayer < 0.0) || (MaxDiff_InputElo_mean_ > StandardDeviationRangeFromInputEloMaximum))
                {
                    Console.WriteLine("Player Input Elo too far from mean or negative (more than 3 sigma), enter another #no.");
                    isLoop = false;
                    isProgramParamsOK = false;
                }
                if (isLoop == false) { break; }
                // break if InputEloPlayer > 999.0 from mean (otherwise unlikely will converge)
                double tooBigEloD = Math.Abs(InputEloPlayer - mean);
                if (tooBigEloD > 999.0)
                {
                    Console.WriteLine("PLayer Input Elo more than 999 pts from mean, enter larger Elo");
                    isLoop = false;
                    isProgramParamsOK = false;
                }
                if (isLoop == false) { break; }
                InputEloPlayerIDInteger = (int)(Math.Round(InputEloPlayer));
                Console.WriteLine("Player Input Elo: {0}", InputEloPlayerIDInteger);

                string inputEloPlayerString_ = String.Format("The Player Elo is: {0}", InputEloPlayerIDInteger);
                myHelperClass.ConsoleOutputStrings.Add(inputEloPlayerString_);

                //
                ChessPlayer targetChessPlayer_ = new ChessPlayer(0, InputEloPlayerIDInteger); //the target chessplayer
                myHelperClass.AddChessPlayerToDict(0, targetChessPlayer_);
                //
                Console.WriteLine();
                m1 = "Input Number of Players in Tournament, (min.>= 5, max. < 500)";
                Console.WriteLine(m1);
                m1 = "(Hit ENTER to give a default value of 9 players)";
                Console.WriteLine(m1);

                try
                {
                    NumPlayersStr = Console.ReadLine();
                }
                catch (System.IO.IOException ex1)
                {
                    Console.WriteLine(ex1.InnerException.Message);
                    isProgramParamsOK = false;
                }
                catch (Exception ex2)
                {
                    Console.WriteLine(ex2.InnerException.Message);
                    isProgramParamsOK = false;
                }

                if (NumPlayersStr == String.Empty)
                {
                    NumPlayersStr = "9";
                }

                bool isResult03InputNumPlayers = Int32.TryParse(NumPlayersStr, out NumPlayers);
                bool isNumPlayersWithinBounds = true;
                if ((NumPlayers < 5) || (NumPlayers >= 500)) { isNumPlayersWithinBounds = false; }
                if ((isResult03InputNumPlayers != true) || (isNumPlayersWithinBounds != true))
                {
                    Console.WriteLine("could not parse # Players, or fewer than 5 or more than 499");
                    isLoop = false;
                    isProgramParamsOK = false;
                }
                if (isLoop == false)
                { break; }
                
                int Bottom_ = 2;
                int modoloResult_ = NumPlayers % Bottom_;
                if (modoloResult_ == 0) //means an even number: this must be made odd # since InputEloPlayer also present so total number must be even
                {
                    NumPlayers = NumPlayers + 1;
                    Console.WriteLine("number players (+1 to make program work): {0}", NumPlayers);
                    string numberPlayers_1 = String.Format("number players (+1 to make program work): {0}", NumPlayers);
                    myHelperClass.ConsoleOutputStrings.Add(numberPlayers_1);
                }
                else
                {
                    //number must be odd to make program work, since InputEloPlayer added to list
                    Console.WriteLine("number players: {0}", NumPlayers);
                }
                //////////
                //enter number of rounds of tournament
                Console.WriteLine();
                m1 = "Input Number of Tournament Games Played Per Player (min.: 4, max.: 30 games)";
                Console.WriteLine(m1);
                m1 = "(Hit ENTER to give a default value of 8 games played)";
                Console.WriteLine(m1); 

                try
                {
                    NumGamesPlayedStr = Console.ReadLine();
                }
                catch (System.IO.IOException ex1)
                {
                    Console.WriteLine(ex1.InnerException.Message);
                    isProgramParamsOK = false;
                }
                catch (Exception ex2)
                {
                    Console.WriteLine(ex2.InnerException.Message);
                    isProgramParamsOK = false;
                }

                if (NumGamesPlayedStr == String.Empty)
                {
                    NumGamesPlayedStr = "8";
                }

                bool isResult03BInputNumGamesPlayed = Int32.TryParse(NumGamesPlayedStr, out NumGamesPlayed);
                bool isNumGamesPlayedInRange_ = true;
                if ((NumGamesPlayed < 4) || (NumGamesPlayed > 30))
                {
                    isNumGamesPlayedInRange_ = false;
                }
                if ((isResult03BInputNumGamesPlayed != true) || (isNumGamesPlayedInRange_ != true))
                {
                    Console.WriteLine("could not parse #game played or greater than 30 or less than four");
                    isLoop = false;
                    isProgramParamsOK = false;
                }
                if (isLoop == false)
                { break; }

                Console.WriteLine("The number of games to be played: {0}", NumGamesPlayed);

                string inputNumGamesPlayedString_ = String.Format("The number games played: {0}", NumGamesPlayed);
               //  
                myHelperClass.ConsoleOutputStrings.Add(inputNumGamesPlayedString_);

                /////
                Console.WriteLine();
                m1 = "Input Number of Iterations for Simulation (max. # <= 1000k)(min. # >= 10)";
                Console.WriteLine(m1);
                m1 = "(Hit ENTER to give a default value of 1000 iterations)";
                Console.WriteLine(m1);

                try
                {
                    IterationsTotalStr = Console.ReadLine();
                }
                catch (System.IO.IOException ex1)
                {
                    Console.WriteLine(ex1.InnerException.Message);
                    isProgramParamsOK = false;
                }
                catch (Exception ex2)
                {
                    Console.WriteLine(ex2.InnerException.Message);
                    isProgramParamsOK = false;
                }

                if (IterationsTotalStr == String.Empty)
                {
                    IterationsTotalStr = "1000";
                }

                bool isResult04InputIterations = Int32.TryParse(IterationsTotalStr, out IterationsTotal);
                bool isIterationsWithinBounds = true;
                if (IterationsTotal < 10 || IterationsTotal > 1000000) { isIterationsWithinBounds = false; }
                if ((isResult04InputIterations != true) || (isIterationsWithinBounds != true))
                {
                    Console.WriteLine("could not parse #iterations or greater than 1000k or less than 10");
                    isLoop = false;
                    isProgramParamsOK = false;
                }
                if (isLoop == false)
                { break; }

                if ((NumPlayers > 100) && (IterationsTotal > 100000)) {isIterationsWithinBounds = false;} //user warned if parameters chosen will result in a long run time
                if (isIterationsWithinBounds == false)
                {
                    Console.WriteLine("For 100k+ iterations, and greater than 100 players,");
                    Console.WriteLine("the simulation can take several minutes.");
                    Console.WriteLine("any output text file is not affected by #iterations or players");
                    Console.WriteLine("text file is limited to a small ~130kb size");
                    Console.WriteLine("if this wait is not acceptable, enter 'Q' to abort (otherwise hit ENTER to continue)");
                    string AbortDueToLargeNumberPlayersAndIntString_ = Console.ReadLine();

                    if (AbortDueToLargeNumberPlayersAndIntString_ == "Q" || AbortDueToLargeNumberPlayersAndIntString_ == "q")
                    {
                        isLoop = false;
                        isProgramParamsOK = false;
                    }
                    
                }
                if (isLoop == false)
                { break; }

                Console.WriteLine("the number of iterations: {0}", IterationsTotal);

                string inputNumIterationsTotalString_ = String.Format("The number iterations: {0}", IterationsTotal);
               //  
                myHelperClass.ConsoleOutputStrings.Add(inputNumIterationsTotalString_);


                //////////////////
                Random rand = new Random();
                double randNormal; //note randNormal is the number that is Gaussian normalized.  Try it in a loop and see...
                List<double> myIntRandomList = new List<double>();
                myIntRandomList.Add(InputEloPlayer); //first number in List is InputEloPlayer
                bool isNegativeElo = false;
                for (int i = 0; i < NumPlayers; i++)
                {
                    double u1 = rand.NextDouble(); // doubles between 0.0 and 1.0
                    double u2 = rand.NextDouble();
                    double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); // random normal (0,1)
                    randNormal = mean + (stdDev * randStdNormal);
                    if (randNormal < 0.0)
                    {
                        isLoop = false; //shows negative Elo meaning Std Dev too big
                        isNegativeElo = true;
                    }
                    myIntRandomList.Add(randNormal);
                }
                if (isNegativeElo == true)
                {
                    Console.WriteLine("The Standard Deviation is too large, resulting in negative Elo.  Pls try again with a smaller Std.Dev.");
                    isProgramParamsOK = false;
                }
                if (isLoop == false)
                { break; }

                Console.WriteLine("Here are the random Elos in Gaussian form: ");
                foreach (double d1 in myIntRandomList)
                {
                    string myD1str_ = String.Format("{0:F0}", d1);
                    Console.Write("{0},", myD1str_);
                }
                Console.WriteLine(".........");
                var sortedElo = myIntRandomList.Select(t => t).OrderBy(t => t);
                Console.WriteLine("........ Gaussian Elo sorted ........... ");
                Console.WriteLine();
                foreach (double d1 in sortedElo)
                {
                    string myD1str_ = String.Format("{0:F0}", d1);
                    Console.Write("{0},", myD1str_);
                }
                Console.WriteLine();
                List<int> List_elo_int = new List<int>();
                foreach (double d1 in sortedElo)
                {
                    List_elo_int.Add((int)Math.Round(d1));
                }

                myHelperClass.HelperClass1FillDictionary(List_elo_int);

                Console.WriteLine("...");
                isLoop = false;
            }
            while (isLoop);
            //////////////////////////////
            //end up here after break
            ////////////////////////////
            if (isProgramParamsOK == true)
            {

                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();  //start stopwatch
                Console.WriteLine();
                Console.WriteLine("...now do chess competition ...");
                Console.WriteLine();
                bool isVerbose_ = true;
                ChessDestiny myChessDestiny = new ChessDestiny(myHelperClass.ChessPlayersDict, InputEloPlayerIDInteger, isVerbose_, NumGamesPlayed, IterationsTotal, stdDev, mean);
                //
                if (myDisplayClass0.isVerbose == true)
                {
                    foreach (string s1 in myDisplayClass0.Display2ListStrings)
                    {
                        myChessDestiny.COutputStrings.Add(s1);
                    }
                }

                foreach (string s2 in myHelperClass.ConsoleOutputStrings)
                {
                    myChessDestiny.COutputStrings.Add(s2);
                }

            //    myChessDestiny.COutputStrings = myHelperClass.ConsoleOutputStrings; //fill up Output strings for eventual text file
                //
                myChessDestiny.PrintChessPlayersDictionary();

                myChessDestiny.ChessCombatFunc(); //(NumGamesPlayed, IterationsTotal);
                stopwatch.Stop(); //stop stopwatch
                Console.WriteLine();

                Console.WriteLine("after {0} rounds per iteration, and {1} iterations", NumGamesPlayed, IterationsTotal);
                Console.WriteLine();
                string numberRoundsItter_ = String.Format("after {0} rounds per iteration, and {1} iterations", NumGamesPlayed, IterationsTotal);
                myChessDestiny.COutputStrings.Add(numberRoundsItter_);

                //Console.WriteLine("   ...print winners...   ");
                string S012a_ = "                   ";
                myChessDestiny.COutputStrings.Add(S012a_);
                myChessDestiny.WinningChessPlayerListPrintout(); //  

                string saBlankString20qerwa_ = String.Empty;
                myChessDestiny.COutputStrings.Add(saBlankString20qerwa_);
                Console.WriteLine();
                Console.WriteLine("Program elapsed time: {0}", stopwatch.Elapsed); //print out elapsed time 
                Console.WriteLine();
                string swTimeElapsed239123_ = string.Format("Program elapsed time: {0}", stopwatch.Elapsed); //
                myChessDestiny.COutputStrings.Add(swTimeElapsed239123_);

                //print out now output strings to text file
                Console.WriteLine("Output results to text file? Type N=No or Y=Yes and hit RETURN (Default = Y)");
                 
                string TextFileOutputYorNstring = String.Empty;
                try
                {
                    TextFileOutputYorNstring = Console.ReadLine();
                    TextFileOutputYorNstring = TextFileOutputYorNstring.ToUpper();
                    if ((TextFileOutputYorNstring != "N"))
                    {
                        myChessDestiny.CreateTextFile();
                        Console.WriteLine("Text file 'SCTWPS.txt' created in current directory");
                    }
                    else
                    {
                        Console.WriteLine("No text file created");
                    }
                }
                catch (System.IO.IOException ex1)
                {
                    Console.WriteLine(ex1.InnerException.Message);
                    isProgramParamsOK = false;
                }
                catch (Exception ex2)
                {
                    Console.WriteLine(ex2.InnerException.Message);
                    isProgramParamsOK = false;
                }
                
            }
            else
            {
                Console.WriteLine("Error in input/abort; please try again.");
            }
            
        Console.WriteLine("Press ENTER key to continue and exit");  
Console.ReadLine();
          

    }

            
}
    }

